package org.kugua.strategy.impl;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.kugua.common.Constants;
import org.kugua.context.CaptchaContext;
import org.kugua.dto.Captcha;
import org.kugua.enums.CaptchaEnum;
import org.kugua.strategy.AbstractSliderCaptchaStrategy;
import org.kugua.utils.RandomUtils;
import org.springframework.util.CollectionUtils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Objects;

/**
 * 滑动类-验证码
 *
 * <p>
 *     缺点：<br/>
 *     1.凹槽和滑块图形需要手动生成，更换图形的话，难度系数比较高 <br/>
 *     2.生成凹槽和滑块四周的圆形不光滑<br/>
 *     3.效果比较差<br/>
 * </p>
 * @author 苦瓜
 * @create 2024-08-26
 * @since 1.0.0
 */
@Slf4j
@Getter
@Setter
@Deprecated
public class SliderCaptchaStrategy extends AbstractSliderCaptchaStrategy {

    /**
     * 默认滑块的大小
     */
    private Integer blockSize = 40;

    /**
     * 滑块周围圆的半径
     */
    private Integer r = 5;

    /**
     * 滑块周围圆的数量
     */
    private Integer circleNum = 1;


    public void setCircleNum(Integer circleNum) {
        if (circleNum > 4) {
            circleNum = 4;
        }
        this.circleNum = circleNum;
    }

    /**
     * 验证码内容填充
     *
     * @param context 内容
     * @return Captcha
     */
    @Override
    public Captcha doFillContext(CaptchaContext context) {
        log.debug("SliderCaptchaStrategy doFillContext started.");
        Captcha slideCaptcha = new Captcha();
        BufferedImage bufferedImage = selectImageIfNeedScale(context);
        BufferedImage slideBlockImage = generateSlideBlock(bufferedImage, blockSize, slideCaptcha, context);
        Graphics2D graphics = context.getGraphics();
        graphics.drawImage(bufferedImage, 0, 0, context.getWidth(), context.getHeight(), null);
        String slider = toBase64(slideBlockImage, getOutImageType());
        slideCaptcha.setSlider(slider);
        return slideCaptcha;
    }

    /**
     * 随机生成滑块
     *
     * @param bufferedImage 背景图
     * @param blockSize     滑块的大小
     */
    protected BufferedImage generateSlideBlock(BufferedImage bufferedImage, Integer blockSize,
                                               Captcha slideCaptcha, CaptchaContext context) {
        log.debug("generate slider block.");
        int minX = r * 2;
        int maxX = bufferedImage.getWidth() - (blockSize + minX * 2) * 2;
        int maxY = bufferedImage.getHeight() - blockSize - minX * 2;
        // 生成滑块位置
        int blockX = RandomUtils.random(maxX);
        int blockY = RandomUtils.random(maxY);
        // 滑块的实际大小 = blockSize
        int realBlockSize = blockSize + 2 * minX;
        BufferedImage slideBlockImage = new BufferedImage(realBlockSize, context.getHeight(),
                BufferedImage.TYPE_INT_RGB);
        Graphics2D slideGr = slideBlockImage.createGraphics();
        // 设置透明度
        BufferedImage compatibleImage = slideGr.getDeviceConfiguration()
                .createCompatibleImage(realBlockSize, context.getHeight(), Transparency.TRANSLUCENT);
        // 设置滑块的颜色
        for (int x = 0; x < realBlockSize; x++) {
            for (int y = 0; y < realBlockSize; y++) {
                if (x < minX || y < minX || x > realBlockSize - minX || y > realBlockSize - minX) {
                    continue;
                }
                int oldRgb = bufferedImage.getRGB(x + blockX, y + blockY);
                compatibleImage.setRGB(x, y, oldRgb);
                bufferedImage.setRGB(x + blockX, y + blockY, Constants.Color.WHITE.getRGB());
            }
        }

        // 随机生成圆形键槽的位置
        int[] circles = randomCircleLocation();
        // 设置背景图中滑块周围的圆的颜色和滑块中圆的颜色
        setCircleImage(bufferedImage, compatibleImage, blockX, blockY, circles);
        // 滑块中心位置
        int answerX = blockX + realBlockSize / 2;
        int answerY = blockY + realBlockSize / 2;
        log.debug("slider location: x:{}, y:{}", answerX, answerY);
        slideCaptcha.setAnswer(List.of(String.format("%s:%s", answerX, answerY)));
        return compatibleImage;
    }

    /**
     * 生成圆形键槽
     *
     * @param bufferedImage   背景图
     * @param compatibleImage 滑块
     * @param blockX          滑块x坐标
     * @param blockY          滑块y坐标
     * @param circles         是否生成圆行键槽
     */
    private void setCircleImage(BufferedImage bufferedImage, BufferedImage compatibleImage,
                                int blockX, int blockY, int[] circles) {
        log.debug("draw circle image in slider block.");
        drawUpCircle(circles[0], bufferedImage, compatibleImage, blockX, blockY);
        drawRightCircle(circles[1], bufferedImage, compatibleImage, blockX, blockY);
        drawDownCircle(circles[2], bufferedImage, compatibleImage, blockX, blockY);
        drawLeftCircle(circles[3], bufferedImage, compatibleImage, blockX, blockY);
    }

    /**
     * 绘制滑块上方的圆形
     *
     * @param isDraw          是否绘制
     * @param compatibleImage 滑块
     * @param bufferedImage   背景图
     * @param blockX          滑块的横坐标
     * @param blockY          滑块的纵坐标
     */
    private void drawUpCircle(int isDraw, BufferedImage bufferedImage, BufferedImage compatibleImage,
                              int blockX, int blockY) {
        if (isDraw != 1) {
            return;
        }
        log.debug("draw circle above the slider.");
        int circleCenterX = compatibleImage.getWidth() / 2;
        int maxY = r * 2;
        System.out.println("up circle: x:" + circleCenterX);
        for (int x = circleCenterX - r; x < circleCenterX + r; x++) {
            for (int y = 0; y < maxY; y++) {
                drawCircle(bufferedImage, compatibleImage, x, y, circleCenterX, r, blockX, blockY);
            }
        }
    }

    /**
     * 绘制滑块右边的圆形
     *
     * @param isDraw          是否绘制
     * @param compatibleImage 滑块
     * @param bufferedImage   背景图
     * @param blockX          滑块的横坐标
     * @param blockY          滑块的纵坐标
     */
    private void drawRightCircle(int isDraw, BufferedImage bufferedImage, BufferedImage compatibleImage,
                                 int blockX, int blockY) {
        if (isDraw != 1) {
            return;
        }
        log.debug("draw circle on the right side of the slider.");
        // 圆心位置
        int circleCenterX = compatibleImage.getWidth() - r;
        // 2024-09-23 调整圆心位置
        int circleCenterY = compatibleImage.getWidth() / 2;

        int startX = circleCenterX - r;
        int startY = circleCenterY - r;
        int endY = circleCenterY + r;

        for (int x = startX; x < compatibleImage.getWidth(); x++) {
            for (int y = startY; y < endY; y++) {
                drawCircle(bufferedImage, compatibleImage, x, y, circleCenterX, circleCenterY, blockX, blockY);
            }
        }
    }

    /**
     * 绘制滑块下边的圆形
     *
     * @param isDraw          是否绘制
     * @param compatibleImage 滑块
     * @param bufferedImage   背景图
     * @param blockX          滑块的横坐标
     * @param blockY          滑块的纵坐标
     */
    private void drawDownCircle(int isDraw, BufferedImage bufferedImage, BufferedImage compatibleImage,
                                int blockX, int blockY) {
        if (isDraw != 1) {
            return;
        }
        log.debug("draw circle below the slider.");
        int circleCenterX = compatibleImage.getWidth() / 2;
        // 2024-09-23 调整计算方式
        int circleCenterY = compatibleImage.getWidth() - r;
        int startX = circleCenterX - r;
        int endX = circleCenterX + r;
        int startY = circleCenterY - r;
        int endY = circleCenterY + r;
        for (int x = startX; x < endX; x++) {
            for (int y = startY; y < endY; y++) {
                drawCircle(bufferedImage, compatibleImage, x, y, circleCenterX, circleCenterY, blockX, blockY);
            }
        }
    }


    /**
     * 绘制滑块左边的圆形
     *
     * @param isDraw          是否绘制
     * @param compatibleImage 滑块
     * @param bufferedImage   背景图
     * @param blockX          滑块的横坐标
     * @param blockY          滑块的纵坐标
     */
    private void drawLeftCircle(int isDraw, BufferedImage bufferedImage, BufferedImage compatibleImage, int blockX, int blockY) {
        if (isDraw != 1) {
            return;
        }
        log.debug("draw circle on the left side of the slider.");
        int circleCenterY = compatibleImage.getWidth() / 2;
        int startY = circleCenterY - r;
        int endY = circleCenterY + r;
        int endX = r * 2;
        for (int x = 0; x < endX; x++) {
            for (int y = startY; y < endY; y++) {
                drawCircle(bufferedImage, compatibleImage, x, y, r, circleCenterY, blockX, blockY);
            }
        }
    }

    /**
     * 绘制圆
     *
     * @param bufferedImage   背景图
     * @param compatibleImage 滑块
     * @param curX            当前点X坐标
     * @param curY            当前点Y坐标
     * @param circleCenterX   圆心X坐标
     * @param circleCenterY   圆心Y坐标
     * @param blockX          滑块X坐标
     * @param blockY          滑块Y坐标
     */
    private void drawCircle(BufferedImage bufferedImage,
                            BufferedImage compatibleImage,
                            int curX, int curY,
                            int circleCenterX, int circleCenterY,
                            int blockX, int blockY) {
        // r的平方
        double targetValue = Math.pow(r, 2);
        // 点是否在圆内，利用数学公式： (x-a)²+(y-b)² <= r²
        boolean isCircleInner = Math.pow(curX - circleCenterX, 2) + Math.pow(curY - circleCenterY, 2) <= targetValue;
        if (!isCircleInner) {
            return;
        }
        compatibleImage.setRGB(curX, curY, bufferedImage.getRGB(blockX + curX, blockY + curY));
        bufferedImage.setRGB(blockX + curX, blockY + curY, Constants.Color.WHITE.getRGB());
    }

    /**
     * 随机生成圆的位置
     *
     * @return Integer[] 圆的位置
     */
    private int[] randomCircleLocation() {
        int[] circle = new int[4];
        // 滑块周围的圆的位置，随机选择4个位置中的blockSize个
        for (int i = 0; i < getCircleNum(); i++) {
            while (true) {
                Integer index = RandomUtils.random(4);
                Integer value = circle[index];
                if (!Objects.equals(value, 1)) {
                    circle[index] = 1;
                    break;
                }
            }
        }
        return circle;
    }

    /**
     * 当前验证码类型
     *
     * @return String
     */
    @Override
    public String getType() {
        return CaptchaEnum.SLIDE.getType();
    }
}
